package ui;

import java.awt.Color;
import java.awt.Component;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseEvent;
import java.awt.event.MouseMotionListener;

import javax.swing.AbstractCellEditor;
import javax.swing.JButton;
import javax.swing.JTable;
import javax.swing.SwingConstants;
import javax.swing.UIManager;
import javax.swing.table.DefaultTableModel;
import javax.swing.table.JTableHeader;
import javax.swing.table.TableCellEditor;
import javax.swing.table.TableCellRenderer;
import javax.swing.table.TableColumn;


/**
 * A custom JTable that sets the majority of the cells to an uneditable state, and
 * allows for certain columns to hold pressable buttons.
 * 
 * @author Aaron Chen
 * @version 3/7/2014
 */
@SuppressWarnings("serial")
public class CustomTable extends JTable {

  /** The row to be rendered in highlighting color, and the row index where that
   * cell's action is to be performed, if any. */
  private int my_mouse_at_row = -1;
  
  /** The column index where that cell's action is to be performed, if any. */
  private int my_mouse_at_column = -1;
  
  /** The columns that will have buttons. */
  private boolean[] my_button_columns;
  
  /** The matrix of actions passed in by the parent component. */
  private ActionListener[][] my_actions;
  
  
  /**
   * Constructs a customized JTable. 
   * @param the_data    The data that goes into each cell.
   * @param the_headers The labels that go into the table header.
   * @param the_bools   The columns that will be button columns.
   * @param the_actions The actions that are associated with the buttons.  More
   * specifically, the actions that are associated with the corresponding cell.
   */
  public CustomTable(final Object[][] the_data, final Object[] the_headers,
                     final boolean[] the_bools, final ActionListener[][] the_actions) {
    super(the_data, the_headers);
    my_actions = the_actions;
    setUpEditableColumns(the_data, the_headers, the_bools);
    setUpTableHeader();
    addMouseMotionListener(new TableMouseListener());
  }
  
  /**
   * Sets up the button columns.
   * @param the_data    The data that goes into each cell.
   * @param the_headers The labels that go into the table header.
   * @param the_bools   The columns that will be button columns.
   */
  private void setUpEditableColumns(final Object[][] the_data, final Object[] the_headers,
                                    final boolean[] the_bools) {
    my_button_columns = new boolean[the_headers.length];
    System.arraycopy(the_bools, 0, my_button_columns, 0, the_headers.length);
    setModel(new DefaultTableModel(the_data, the_headers) {
      @Override
      public boolean isCellEditable(final int the_row_index, final int the_col_index) {
        return my_button_columns[the_col_index];
      }
    });
    for (int i = 0; i < my_button_columns.length; i++) {
      if (my_button_columns[i]) {
        final TableColumn column = getColumnModel().getColumn(i);
        column.setCellRenderer(new ButtonRenderer());
        column.setCellEditor(new ButtonEditor());
      }
    }
  }
  
  /**
   * Sets up the table header's properties.
   */
  private void setUpTableHeader() {
    final JTableHeader header = this.getTableHeader();
    header.setBackground(Standards.COLOR_TABLE_HEADER);
    header.setReorderingAllowed(false);
  }
  
  /**
   * {@inheritDoc}
   */
  @Override
  public Component prepareRenderer(final TableCellRenderer the_renderer,
                                   final int the_row, final int the_col) {
    final Component c = super.prepareRenderer(the_renderer, the_row, the_col);
    if (!my_button_columns[the_col]) {
      if (the_row == my_mouse_at_row) {
        c.setBackground(Color.YELLOW);
      } else {
        if (the_row % 2 == 0) {
          c.setBackground(Standards.COLOR_TABLE_ODD_ROW);
        } else {
          c.setBackground(Standards.COLOR_TABLE_EVEN_ROW);
        }
      }
    }
    return c;
  }
  
  /**
   * A custom class that tells the renderer how the cell would look like while
   * the cell is not selected for editing.
   * 
   * @author Aaron Chen
   * @version 3/7/2014
   */
  private class ButtonRenderer extends JButton implements TableCellRenderer {

    /**
     * {@inheritDoc}
     */
    @Override
    public Component getTableCellRendererComponent(final JTable the_table,
                                                   final Object the_value,
                                                   final boolean the_selected,
                                                   final boolean the_focus,
                                                   final int the_row, final int the_col) {
      if (the_selected) {
        setForeground(the_table.getSelectionForeground());
        setBackground(the_table.getSelectionBackground());
      } else {
        setForeground(the_table.getForeground());
        setBackground(UIManager.getColor("Button.background"));
      }
      String str = "";
      if (the_value != null) {
        str = the_value.toString();
      }
      setText(str);
      setHorizontalAlignment(SwingConstants.LEFT);
      return this;
    } 
  }
  
  /**
   * A custom class that tells the renderer how the cell would look like while
   * the cell is selected for editing.
   * 
   * @author Aaron Chen
   * @version 3/7/2014
   */
  private class ButtonEditor extends AbstractCellEditor implements TableCellEditor {

    /** The button that the user interacts with. */
    private JButton my_button;
    
    /** The label for the button. */
    private String my_label;
    
    /** Whether the button is pressed or not.  May or may not correspond to the
     * actual state of the button. */
    private boolean my_pushed;
    
    
    /**
     * Creates a specialized TableCellEditor that mimics a button when the cell
     * is selected. 
     */
    public ButtonEditor() {
      super();
      my_button = new JButton();
      my_button.setHorizontalAlignment(SwingConstants.LEFT);
      my_button.addActionListener(new ActionListener() {
        @Override
        public void actionPerformed(final ActionEvent the_e) {
          if (my_actions[my_mouse_at_row][my_mouse_at_column] != null) {
            my_actions[my_mouse_at_row][my_mouse_at_column].actionPerformed(the_e);
          }
        }
      });
    }
    
    /**
     * {@inheritDoc}
     */
    @Override
    public Object getCellEditorValue() {
      if (my_pushed) {
        System.out.println("wtf");
      }
      my_pushed = false;
      return my_label;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public Component getTableCellEditorComponent(final JTable the_table,
                                                 final Object the_value,
                                                 final boolean the_selected,
                                                 final int the_row, final int the_col) {
      if (the_selected) {
        my_button.setForeground(the_table.getSelectionForeground());
        my_button.setBackground(the_table.getSelectionBackground());
      } else {
        my_button.setForeground(the_table.getForeground());
        my_button.setBackground(the_table.getBackground());
      }
      my_label = "";
      if (the_value != null) {
        my_label = the_value.toString();
      }
      my_button.setText(my_label);
      my_pushed = true;
      return my_button;
    }
    
    /**
     * {@inheritDoc}
     * <p>
     * Additionally, sets the button press state to false.
     */
    @Override
    public boolean stopCellEditing() {
      my_pushed = false;
      return super.stopCellEditing();
    }
  }
  
  /**
   * The mouse motion event listener for the table.
   * 
   * @author Aaron Chen
   * @version 3/7/2014
   */
  private class TableMouseListener implements MouseMotionListener { 
  
    /**
     * Does nothing.
     * @param the_e not used.
     */
    @Override
    public void mouseDragged(final MouseEvent the_e) {
      //mouseMoved(the_e);
    }
  
    /**
     * Sets the row index at where the mouse is pointing.
     * @param the_e The mouse event.
     */
    @Override
    public void mouseMoved(final MouseEvent the_e) {
      my_mouse_at_row = rowAtPoint(the_e.getPoint());
      my_mouse_at_column = columnAtPoint(the_e.getPoint());
    }
  }
}
